namespace Brjnk.Maps
{
    using System;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Linq;

    /// <summary>
    /// </summary>
    public class UsageLogger : IDownloadLogger, IDbUsageLogger, System.ComponentModel.INotifyPropertyChanged
    {
        private ObservableCollection<UsagePerDomain> domains =
            new ObservableCollection<UsagePerDomain>();

        private ObservableCollection<CacheUsage> caches =
            new ObservableCollection<CacheUsage>();

        private ReadOnlyObservableCollection<UsagePerDomain> wrapperDomains;

        private ReadOnlyObservableCollection<CacheUsage> wrapperCaches;

        public UsageLogger()
        {
            wrapperDomains = new ReadOnlyObservableCollection<UsagePerDomain>(domains);
            wrapperCaches = new ReadOnlyObservableCollection<CacheUsage>(caches);
        }

        public void LogDownload(Uri uri, long bytesCount, TimeSpan time)
        {
            string domain = uri.Host;
            GetUsageForDomain(domain).IncrementBytes(bytesCount);
        }

        public void LogDownloadCompleted(string domain, bool success)
        {
            if (success == false) return;
            GetUsageForDomain(domain).IncrementCount();
        }

        public void LogCacheState(string cacheName, int filesCount, string info)
        {
            var usage = GetDbUsage(cacheName);
            usage.TotalFilesCount = filesCount;
            usage.State = info;
        }

        public void LogResolvedFromCache(string cacheName)
        {
            GetDbUsage(cacheName).ResolvedCount++;
        }

        private UsagePerDomain GetUsageForDomain(string domain)
        {
            var usagePerDomain = domains.FirstOrDefault(o => string.Equals(domain, o.Domain, StringComparison.InvariantCultureIgnoreCase));
            if (usagePerDomain == null)
            {
                usagePerDomain = new UsagePerDomain(domain);
                domains.Add(usagePerDomain);
                return usagePerDomain;
            }
            else
            {
                return usagePerDomain;
            }
        }

        private CacheUsage GetDbUsage(string cache)
        {
            var cacheUsage = caches.FirstOrDefault(o => string.Equals(cache, o.Name, StringComparison.InvariantCultureIgnoreCase));
            if (cacheUsage == null)
            {
                cacheUsage = new CacheUsage(cache);
                caches.Add(cacheUsage);
                return cacheUsage;
            }
            else
            {
                return cacheUsage;
            }
        }

        public INotifyCollectionChanged Domains
        {
            get { return wrapperDomains; }
        }

        public INotifyCollectionChanged Caches
        {
            get { return wrapperCaches; }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class UsagePerDomain : System.ComponentModel.INotifyPropertyChanged
    {
        private string domain;

        public UsagePerDomain(string domain)
        {
            this.domain = domain;
        }

        public string Domain { get { return domain; } }

        public void IncrementCount()
        {
            FilesCount++;
        }

        public void IncrementBytes(long value)
        {
            Bytes += value;
        }

        private long bytes;

        public long Bytes
        {
            get { return bytes; }
            protected set { bytes = value; OnPropertyChanged("Bytes"); }
        }

        private int filesCount;

        public int FilesCount
        {
            get { return filesCount; }
            protected set { filesCount = value; OnPropertyChanged("FilesCount"); }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class CacheUsage : System.ComponentModel.INotifyPropertyChanged
    {
        private string name;

        public CacheUsage(string dbName)
        {
            this.name = dbName;
        }

        public string Name
        {
            get { return name; }
        }

        private int resolvedCount;

        public int ResolvedCount
        {
            get { return resolvedCount; }
            set { resolvedCount = value; OnPropertyChanged("ResolvedCount"); }
        }

        private int totalFilesCount;

        public int TotalFilesCount
        {
            get { return totalFilesCount; }
            set { totalFilesCount = value; OnPropertyChanged("TotalFilesCount"); }
        }

        private string state;

        public string State
        {
            get { return state; }
            set { state = value; OnPropertyChanged("State"); }
        }

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}